package horncomb.bpm.process.instance;

import horncomb.bpm.BpmContextHolder;
import horncomb.bpm.process.core.EndState;
import horncomb.bpm.process.core.ExecutionContext;
import horncomb.bpm.process.core.ProcessInstance;
import horncomb.bpm.process.core.StateDefinition;
import horncomb.bpm.process.core.Token;
import horncomb.bpm.process.core.Transition;
import horncomb.bpm.process.core.entity.TokenEntity;
import horncomb.bpm.process.def.state.ReferedProcessStateDefinition;

import java.util.Date;
import java.util.Stack;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * @author Daniel
 */
public class DefaultToken implements Token, TokenEntity {
	private static final long serialVersionUID = 7714645582087482942L;
	protected TokenEntity entity;

	transient private Stack<String> nestedStateDefinitionIdStack;

	// transient private Stack<String> nestedPrestateDefinitionIdStack;

	/**
	 *
	 */
	public DefaultToken() {
		super();
	}

	public DefaultToken(TokenEntity tokenEntity) {
		super();
		Assert.notNull(tokenEntity);
		this.nestedStateDefinitionIdStack = null;
		// this.nestedPrestateDefinitionIdStack = null;
		this.entity = tokenEntity;

	}

	public DefaultToken(TokenEntity tokenEntity, String processInstanceId) {
		super();
		Assert.notNull(tokenEntity);
		this.nestedStateDefinitionIdStack = null;
		// this.nestedPrestateDefinitionIdStack = null;
		this.entity = tokenEntity;
		this.entity.setProcessInstanceId(processInstanceId);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((entity == null) ? 0 : entity.hashCode());
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Token other = (Token) obj;
		if (entity == null) {
			if (other.getEntity() != null)
				return false;
		} else if (!entity.equals(other.getEntity()))
			return false;
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public TokenEntity getEntity() {
		return this.entity;
	}

	/**
	 * @param entity
	 *            the entity to set
	 */
	public void setEntity(TokenEntity entity) {
		this.entity = entity;
	}

	public ProcessInstance getProcessInstance() {
		return BpmContextHolder.getContext().getProcessManagementService()
				.findProcessInstance(this.getProcessInstanceId());
	}

	// /**
	// * {@inheritDoc}
	// */
	// public StateDefinition getPrestate() {
	//
	// }

	// /**
	// * {@inheritDoc}
	// */
	// public void setPrestate(StateDefinition stateDef) {
	// this.tokenEntity.setPrestateId(stateDef.getId());
	// }

	/**
	 * {@inheritDoc}
	 */
	public StateDefinition getState() {
		StateDefinition stateDef = null;
		if (!this.getNestedStateDefinitionIdStack().isEmpty()) {
			if (this.getNestedStateDefinitionIdStack().size() == 1) {
				stateDef = this.getProcessInstance().getProcessDefinition()
						.findStateDefinition(
								this.getNestedStateDefinitionIdStack().peek());
			} else {
				// this.getNestedStateDefinitionIdStack().size()>1
				stateDef = this.getProcessInstance().getProcessDefinition()
						.findStateDefinition(
								this.getNestedStateDefinitionIdStack().get(
										this.getNestedStateDefinitionIdStack()
												.size() - 2));
				if (stateDef instanceof ReferedProcessStateDefinition) {
					stateDef = ((ReferedProcessStateDefinition) stateDef)
							.getInnerProcessDefinition().findStateDefinition(
									this.getNestedStateDefinitionIdStack()
											.peek());
				} else {
					// TODO throw IllegalStateException
				}
			}
		}
		return stateDef;
	}

	/**
	 * {@inheritDoc}
	 */
	public void setState(StateDefinition state) {
		if (state == null && !this.getNestedStateDefinitionIdStack().isEmpty()) {
			// 离开状态
			this.getNestedStateDefinitionIdStack().pop();
			int idx = StringUtils.hasText(this.entity.getStateId()) ? this.entity
					.getStateId().lastIndexOf('|')
					: 0;
			if (idx > 0) {
				this.entity.setStateId(this.entity.getStateId().substring(0,
						idx - 1));
			} else {
				this.entity.setStateId(null);
			}
			this.setEnteredDate(null);
		} else if (state != null) {
			if (state instanceof EndState
					&& this.getNestedStateDefinitionIdStack().size() > 0) {
				// 进入结束状态
			} else {
				this.getNestedStateDefinitionIdStack().push(state.getId());
				this.entity
						.setStateId(StringUtils.hasText(this.entity
								.getStateId()) ? (this.entity.getStateId()
								+ "|" + state.getId()) : state.getId());
			}
			this.setEnteredDate(new Date());
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void start(Object contextModel) {

	}

	/**
	 * {@inheritDoc}
	 */
	public void performTransition(Transition transition, Object contextModel) {
		ExecutionContext context = new DefaultExcutionContext(this,
				contextModel);
		context.setTransition(transition);
		transition.transit(context);
	}

	/**
	 * @return the nestedStateDefinitionIdStack
	 */
	protected Stack<String> getNestedStateDefinitionIdStack() {
		if (this.nestedStateDefinitionIdStack == null) {
			this.nestedStateDefinitionIdStack = new Stack<String>();
			if (StringUtils.hasText(this.entity.getStateId())) {
				for (String id : this.entity.getStateId().split("\\|")) {
					this.nestedStateDefinitionIdStack.push(id);
				}
			}
		}
		return this.nestedStateDefinitionIdStack;
	}

	// /**
	// * @return the nestedStateDefinitionIdStack
	// */
	// protected Stack<String> getNestedPrestateDefinitionIdStack() {
	// if (this.nestedPrestateDefinitionIdStack == null) {
	// this.nestedPrestateDefinitionIdStack = new Stack<String>();
	// if (StringUtils.hasText(this.tokenEntity.getPrestateId())) {
	// for (String id : this.tokenEntity.getPrestateId().split("|")) {
	// this.nestedPrestateDefinitionIdStack.push(id);
	// }
	// }
	// }
	// return this.nestedPrestateDefinitionIdStack;
	// }

	/**
	 * @return the id
	 */
	public String getId() {
		return this.entity.getId();
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(String id) {
		this.entity.setId(id);
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return this.entity.getName();
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.entity.setName(name);
	}

	/**
	 * @return the stateEntered
	 */
	public Date getEnteredDate() {
		return this.entity.getEnteredDate();
	}

	/**
	 * @param stateEntered
	 *            the stateEntered to set
	 */
	public void setEnteredDate(Date stateEntered) {
		this.entity.setEnteredDate(stateEntered);
	}

	/**
	 * @return the processInstanceId
	 */
	public String getProcessInstanceId() {
		return this.entity.getProcessInstanceId();
	}

	/**
	 * {@inheritDoc}
	 */
	public void setProcessInstanceId(String procInstId) {
		this.entity.setProcessInstanceId(procInstId);
	}

	/**
	 * {@inheritDoc}
	 */
	public String getStateId() {
		return this.entity.getStateId();
	}

	/**
	 * {@inheritDoc}
	 */
	public void setStateId(String state) {
		this.entity.setStateId(state);
	}

	/**
	 * {@inheritDoc}
	 */
	public void setBizEntityId(String bizEntityId) {
		this.entity.setBizEntityId(bizEntityId);
	}

	/**
	 * {@inheritDoc}
	 */
	public String getBizEntityId() {
		return this.entity.getBizEntityId();
	}

	/**
	 * {@inheritDoc}
	 */
	public void setBizDefinitionId(String bizDefinitionId) {
		this.entity.setBizDefinitionId(bizDefinitionId);
	}

	/**
	 * {@inheritDoc}
	 */
	public String getBizDefinitionId() {
		return this.entity.getBizDefinitionId();
	}
}